#include "Chunk.h"
#include "World.h"
#include "Cell.h"
#include "../Managers/GameManager.h"
#include "../GameObjects/Player.h"
#include "../Math/Vec3f.h"
#include "../GameObjects/Tree.h"
#include "../Maps/TerrainType.h"

#include <libtcod\libtcod.hpp>

#include <sstream>

Chunk::Chunk(const Vec2i &viewport, const Vec2i &pos)
	: generated(false), chunk_size(viewport), pos(pos)
{
	lower_bounds = pos - chunk_size/2;
	upper_bounds = pos + chunk_size/2;

	cells.resize(chunk_size.x() * chunk_size.y());
}

Chunk::~Chunk()
{
}

void Chunk::generate(const TCODNoisePtr &world_noise, const TCODRandomPtr &world_ran, float mountain_height, float hill_height, float land_height, int *smooth_dx, int *smooth_dy, float *smooth_weights)
{
	height_map = std::make_shared<TCODHeightMap>(chunk_size.x(), chunk_size.y());
	TCODHeightMap hm1(chunk_size.x(), chunk_size.y());
	TCODHeightMap hm2(chunk_size.x(), chunk_size.y());
	TCODHeightMap hm3(chunk_size.x(), chunk_size.y());
	float resolution = 1.0f;
	hm1.addFbm(world_noise.get(), 0.00101f*resolution, 0.00101f*resolution, pos.x(),pos.y(), 8, 0.0f, mountain_height);
	hm2.addFbm(world_noise.get(), 0.0196f*resolution, 0.0196f*resolution, pos.x(),pos.y(), 8, 0.0f, land_height);
	height_map->add(&hm2, &hm1);
	height_map->add(&hm3, height_map.get());
	height_map->addFbm(world_noise.get(), 0.0403f*resolution, 0.0403f*resolution, pos.x(),pos.y(), 8, 0.0f, hill_height);
	
	//height_map->kernelTransform(9, smooth_dx, smooth_dy, smooth_weights, 0.0f, mountain_height+land_height);

	for(int y = 0; y < chunk_size.y(); y++)
	{
		for(int x = 0; x < chunk_size.x(); x++)
		{
			auto index = x + y * chunk_size.x();
			cells[index] = std::make_shared<Cell>(Vec3f(x+lower_bounds.x(), Vec3f::clampf(height_map->getValue(x,y), -2.0f, mountain_height), y+lower_bounds.y()), height_map->getSlope(x,y));
		}
	}

	for(int y = 0; y < chunk_size.y(); y++)
	{
		for(int x = 0; x < chunk_size.x(); x++)
		{
			auto index = x + y * chunk_size.x();
			auto &cell = cells[index];

			if(cell->getTerrainType()->id < TERRAIN_DIRT && cell->getTerrainType()->id > TERRAIN_GROUND_FROZEN)
			{
				auto chance = world_ran->get(0.0, 1.0);
				if(chance < 0.05)
				{
					auto radius = world_ran->getInt(1,5);
					bool plant_tree = true;
					for(unsigned int i = 0; i < radius; i++)
					{
						int dx = x-i;
						if(dx >= 0)
						{
							auto index = (dx)+(y)*chunk_size.x();
							if(cells[index]->isWalkable() == false)
								plant_tree = false;
							}

						int dy = y-i;
						if(dy >= 0)
						{			
							auto index = (x)+(dy)*chunk_size.x();
							if(cells[index]->isWalkable() == false)
								plant_tree = false;
						}

						if(dx >= 0 && dy >= 0)
						{
							auto index = (dx)+(dy)*chunk_size.x();
							if(cells[index]->isWalkable() == false)
								plant_tree = false;
						}
					}

					if(plant_tree)
					{
						auto tree = std::make_shared<Tree>(radius);
						
						cell->setWalkable(false);
						cell->addGameObject(tree);
						tree->setPos(cell->getPos2d());
					}
				}
			}
		}
	}


	generated = true;
}

void Chunk::render()
{
	auto viewport = GameManager::getSingleton()->getMapViewport();
	auto &players_lower_bounds = GameManager::getSingleton()->getPlayer()->getLowerBounds();
	auto &players_upper_bounds = GameManager::getSingleton()->getPlayer()->getUpperBounds();

	Vec2i overlap;
	bool count_x_from_left, count_y_from_top;
	if(Vec2i::overlap(lower_bounds, upper_bounds, players_lower_bounds, players_upper_bounds, overlap, count_x_from_left, count_y_from_top) == false)
		return; //This chunk didn't overlap with the player's viewport, so there's nothing to render!

	/*std::stringstream ss;
	ss << overlap.x() << ", " << overlap.y();
	if(world_position == World::WP_CENTER)
		GameManager::getSingleton()->getStoryConsole()->print(0,3, ("CENTER: " + ss.str()).c_str());
	else if(world_position == World::WP_WEST)
		GameManager::getSingleton()->getStoryConsole()->print(0,4, ("WEST: " + ss.str()).c_str());
	else if(world_position == World::WP_NORTHWEST)
		GameManager::getSingleton()->getStoryConsole()->print(0,5, ("NORTHWEST: " + ss.str()).c_str());
	else if(world_position == World::WP_NORTH)
		GameManager::getSingleton()->getStoryConsole()->print(0,6, ("NORTH: " + ss.str()).c_str());
	else if(world_position == World::WP_NORTHEAST)
		GameManager::getSingleton()->getStoryConsole()->print(0,7, ("NORTHEAST: " + ss.str()).c_str());
	else if(world_position == World::WP_EAST)
		GameManager::getSingleton()->getStoryConsole()->print(0,8, ("EAST: " + ss.str()).c_str());
	else if(world_position == World::WP_SOUTHEAST)
		GameManager::getSingleton()->getStoryConsole()->print(0,9, ("SOUTHEAST: " + ss.str()).c_str());
	else if(world_position == World::WP_SOUTH)
		GameManager::getSingleton()->getStoryConsole()->print(0,10, ("SOUTH: " + ss.str()).c_str());
	else if(world_position == World::WP_SOUTHWEST)
		GameManager::getSingleton()->getStoryConsole()->print(0,11, ("SOUTHWEST: " + ss.str()).c_str());*/

	int x_start = 0; int x_end = overlap.x();
	int y_start = 0; int y_end = overlap.y();
	if(count_x_from_left == false)	{ (x_start = chunk_size.x() - 1); (x_end = (chunk_size.x()-1) - overlap.x()); }
	if(count_y_from_top == false)	{ (y_start = chunk_size.y() - 1); (y_end = (chunk_size.y()-1) - overlap.y()); }
	
	//Render all the cells that overlap the player's viewport!
	for(auto y = y_start; (count_y_from_top ? y < y_end : y > y_end); (count_y_from_top ? y++ : y--))
	{
		for(auto x = x_start; (count_x_from_left ? x < x_end : x > x_end); (count_x_from_left ? x++ : x--))
		{
			auto index = x + y * chunk_size.x();
			cells[index]->render();
		}
	}
}

const CellPtr &Chunk::getCell(Vec2i pos) const
{
	for(auto cell : cells)
		if(cell->getPos2d() == pos)
			return cell;
	return nullptr;
}